home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / scheme / pcscheme / geneva / sources.exe / SOURCES / LISP / IO.LSP < prev    next >
Encoding:
Text File  |  1993-10-25  |  18.9 KB  |  497 lines

  1. ;; PC Scheme Common Lisp Compatibility Package
  2. ;;
  3. ;; (c) Copyright 1990 Carl W. Hoffman.  All rights reserved.
  4. ;;
  5. ;; This file may be freely copied, distributed, or modified for non-commercial
  6. ;; use provided that this copyright notice is not removed.  For further
  7. ;; information about other utilities for Common Lisp or Scheme, contact the
  8. ;; following address:
  9. ;;
  10. ;;   Carl W. Hoffman, 363 Marlborough Street, Boston, MA 02115, U.S.A.
  11. ;;   Internet: CWH@AI.MIT.EDU    CompuServe: 76416,3365    Fax: 617-262-4284
  12.  
  13. ;; Streams and I/O
  14.  
  15. (defun-clcp display-substring (raw-stream string start end)
  16.   (if (and (null start) (null end))
  17.       (display string raw-stream)
  18.       (progn
  19.         (setq start (or start 0))
  20.         (setq end (or end (length string)))
  21.         (do ((i start (1+ i)))
  22.             ((= i end))
  23.           (scheme-write-char (char string i) raw-stream)))))
  24.  
  25. (defun-clcp make-encapsulated-input-stream (raw-stream)
  26.   (let ((unch nil))
  27.     (lambda (op . args)
  28.       (case op
  29.         (direction
  30.           'input)
  31.         (close
  32.           (close-input-port raw-stream))
  33.         (read-char
  34.           (if unch
  35.               (prog1 unch (setq unch nil))
  36.               (scheme-read-char raw-stream)))
  37.         (un-read-char
  38.           (if unch
  39.               (error "Attempt to UNREAD-CHAR twice on the stream ~S."
  40.                      raw-stream))
  41.           (setq unch (car args)))
  42.         (peek-char
  43.           (or unch
  44.               (progn (setq unch (scheme-read-char raw-stream))
  45.                      unch)))))))
  46.  
  47. (defun-clcp make-encapsulated-output-stream (raw-stream)
  48.   (lambda (op . args)
  49.     (case op
  50.       (direction
  51.         'output)
  52.       (close
  53.         (close-output-port raw-stream))
  54.       (fresh-line
  55.         (scheme-fresh-line raw-stream))
  56.       (write-char
  57.         (scheme-write-char (car args) raw-stream))
  58.       (write-string
  59.         (apply display-substring raw-stream args)))))
  60.  
  61. (defun make-string-input-stream (string)
  62.   (make-encapsulated-input-stream (open-input-string string)))
  63.  
  64. ;; This could make effective use of resources.
  65.  
  66. (defun make-string-output-stream ()
  67.   (let ((buffer (make-string 50))
  68.         (index 0))
  69.     (flet ((assure-buffer-size (n)
  70.              (let* ((buffer-size (string-length buffer))
  71.                     (new-size (+ index n)))
  72.                (when (> new-size buffer-size)
  73.                  (let ((new-buffer (make-string (* 2 new-size))))
  74.                    (%%replace-string new-buffer buffer 0 0 buffer-size)
  75.                    (setq buffer new-buffer))))))
  76.       (lambda (op . args)
  77.         (case op
  78.           (direction
  79.             'output)
  80.           (close
  81.             (if (= index 0)
  82.                 ""
  83.                 (let ((final-string (make-string index)))
  84.                   (%%replace-string final-string buffer 0 0 index)
  85.                   (setq index 0)
  86.                   final-string)))
  87.           (fresh-line
  88.             (when (and (> index 0)
  89.                        (not (char= (char buffer (1- index)) #\newline)))
  90.               (assure-buffer-size 1)
  91.               (string-set! buffer index #\newline)
  92.               (incf index)
  93.               t))
  94.           (write-char
  95.             (assure-buffer-size 1)
  96.             (string-set! buffer index (car args))
  97.             (incf index))
  98.           (write-string
  99.             (let* ((output-string (first args))
  100.                    (start         (or (second args) 0))
  101.                    (end           (or (third args) (length output-string)))
  102.                    (output-size   (- end start)))
  103.               (assure-buffer-size output-size)
  104.               (%%replace-string buffer output-string index start output-size)
  105.               (incf index output-size))))))))
  106.  
  107. (defmacro with-open-stream (stream-description &body body)
  108.   (unless (and (listp stream-description)
  109.                (cdr stream-description)
  110.                (null (cddr stream-description)))
  111.     (error "The first argument to WITH-OPEN-STREAM must be ~
  112.             a pattern of the form (VAR STREAM)."))
  113.   (let ((stream-var (car stream-description)))
  114.     (unless (symbolp stream-var)
  115.       (error "The stream variable argument to WITH-OPEN-STREAM ~
  116.               is not a symbol."))
  117.     (if (member stream-var '(*standard-input* standard-output*))
  118.         `(let ((.temp. ,stream-var))
  119.            (setq ,stream-var ,(cadr stream-description))
  120.            (prog1 (progn . ,body)
  121.                   (close ,stream-var)
  122.                   (setq ,stream-var .temp.)))
  123.         `(let ((,stream-var ,(cadr stream-description)))
  124.            (prog1 (progn . ,body)
  125.                   (close ,stream-var))))))
  126.  
  127. (defmacro with-input-from-string (stream-description &body body)
  128.   (unless (and (listp stream-description)
  129.                (cdr stream-description)
  130.                (null (cddr stream-description)))
  131.     (error "The first argument to WITH-INPUT-FROM-STRING must be ~
  132.             a pattern of the form (VAR STRING)."))
  133.   (let ((stream-var (car stream-description))
  134.         (string-form (cadr stream-description)))
  135.     (unless (symbolp stream-var)
  136.       (error "The stream variable argument to WITH-INPUT-FROM-STRING ~
  137.               is not a symbol."))
  138.     `(with-open-stream (,stream-var (make-string-input-stream ,string-form))
  139.        . ,body)))
  140.  
  141. (defmacro with-output-to-string (stream-description &body body)
  142.   (unless (and (listp stream-description)
  143.                (or (null (cdr stream-description))
  144.                    (null (cddr stream-description))))
  145.     (error "The first argument to WITH-OUTPUT-TO-STRING must be ~
  146.             a pattern of the form (VAR) or (VAR STRING)."))
  147.   (unless (null (cdr stream-description))
  148.     (error "The two-argument form of WITH-OUTPUT-TO-STRING is not ~
  149.             yet supported."))
  150.   (let ((stream-var (car stream-description)))
  151.     (unless (symbolp stream-var)
  152.       (error "The stream variable argument to WITH-OUTPUT-TO-STRING ~
  153.               is not a symbol."))
  154.     `(with-open-stream (,stream-var (make-string-output-stream))
  155.        ,@body
  156.        (get-output-stream-string ,stream-var))))
  157.  
  158. (defvar *standard-input*  (current-input-port))
  159. (defvar *standard-output* (current-output-port))
  160. (defvar *error-output*    (current-output-port))
  161.  
  162. (defvar *query-io*    'console)
  163. (defvar *debug-io*    'console)
  164. (defvar *terminal-io* 'console)
  165.  
  166. (defun streamp (object)
  167.   (or (null object)
  168.       (port? object)
  169.       (and (procedure? object)
  170.            (funcall object 'direction)
  171.            t)))
  172.  
  173. (defun check-stream (procedure stream)
  174.   (unless (streamp stream)
  175.     (error "The argument to ~A, ~S, is not a stream"
  176.            procedure stream)))
  177.  
  178. ;; CL requires that the arguments to INPUT-STREAM-P and OUTPUT-STREAM-P
  179. ;; be streams.
  180.  
  181. (defun input-stream-p (stream)
  182.   (check-stream 'input-stream-p stream)
  183.   (or (null stream)
  184.       (input-port? stream)
  185.       (and (procedure? stream)
  186.            (memq (funcall stream 'direction) '(input bidirectional))
  187.            t)))
  188.  
  189. (defun output-stream-p (stream)
  190.   (check-stream 'output-stream-p stream)
  191.   (or (null stream)
  192.       (output-port? stream)
  193.       (and (procedure? stream)
  194.            (memq (funcall stream 'direction) '(output bidirectional))
  195.            t)))
  196.  
  197. (defun-clcp check-input-stream (procedure stream &optional cl-only?)
  198.   (unless (and (streamp stream) (input-stream-p stream))
  199.     (error "The argument to ~A, ~S, is not an input stream."
  200.            procedure stream))
  201.   (when (and cl-only? (not (procedure? stream)))
  202.     (error "The stream ~S does not support the ~A operation."
  203.            stream procedure)))
  204.  
  205. (defun-clcp check-output-stream (procedure stream &optional cl-only?)
  206.   (unless (and (streamp stream) (output-stream-p stream))
  207.     (error "The argument to ~A, ~S, is not an output stream."
  208.            procedure stream))
  209.   (when (and cl-only? (not (procedure? stream)))
  210.     (error "The stream ~S does not support the ~A operation."
  211.            stream procedure)))
  212.  
  213. (defun close (stream)
  214.   (check-stream 'close stream)
  215.   (cond ((input-port? stream)
  216.          (close-input-port stream))
  217.         ((output-port? stream)
  218.          (close-output-port stream))
  219.         (else
  220.          (funcall stream 'close))))
  221.  
  222. ;; It might be better to use a different message name than CLOSE for this
  223. ;; function.  STRING-OUTPUT-STREAM can just ignore CLOSE, since it might be
  224. ;; sent from places we don't expect it.
  225.  
  226. (defun get-output-stream-string (string-output-stream)
  227.   (check-output-stream 'get-output-stream-string string-output-stream t)
  228.   (funcall string-output-stream 'close))
  229.  
  230. (defun check-eof (thing input-stream eof-error-p eof-value)
  231.   (cond ((not (eof-object? thing))
  232.          thing)
  233.         (eof-error-p
  234.           (error "EOF reached on the input stream ~S." input-stream))
  235.         (t
  236.           eof-value)))
  237.  
  238. (defun read-char (&optional input-stream (eof-error-p t) eof-value)
  239.   (unless input-stream
  240.     (setq input-stream *standard-input*))
  241.   (check-input-stream 'read-char input-stream)
  242.   (check-eof
  243.     (if (input-port? input-stream)
  244.         (scheme-read-char input-stream)
  245.         (funcall input-stream 'read-char))
  246.     input-stream eof-error-p eof-value))
  247.  
  248. (defun un-read-char (char &optional input-stream)
  249.   (unless input-stream
  250.     (setq input-stream *standard-input*))
  251.   (check-type char character)
  252.   (check-input-stream 'un-read-char input-stream t)
  253.   (funcall input-stream 'un-read-char char))
  254.  
  255. (defun peek-char (&optional peek-type input-stream (eof-error-p t) eof-value)
  256.   (unless input-stream
  257.     (setq input-stream *standard-input*))
  258.   (check-input-stream 'peek-char input-stream t)
  259.   (check-eof
  260.     (cond ((or (eq peek-type nil) (eq peek-type 'nil))
  261.            (funcall input-stream 'peek-char))
  262.           ((or (eq peek-type t) (eq peek-type 't))
  263.            (do ((ch (funcall input-stream 'peek-char)
  264.                     (funcall input-stream 'peek-char)))
  265.                ((or (eof-object? ch)
  266.                     (not (member ch '(#\space #\tab #\newline))))
  267.                 ch)
  268.              (funcall input-stream 'read-char))))
  269.     input-stream eof-error-p eof-value))
  270.  
  271. ;; SCHEME-READ accepts an arbitrary number of arguments, but apparently only
  272. ;; looks at the first one.  I guess somebody forgot to check for too many
  273. ;; arguments.
  274.  
  275. ;; READ is compatible with Scheme when called with fewer than two arguments.
  276. ;; When called with two or more arguments, it is compatible with Common Lisp.
  277. ;; It can't be made completely compatible with Common Lisp since that breaks
  278. ;; PC Scheme.
  279.  
  280. (defun read (&optional input-stream (eof-error-p 'scheme) eof-value)
  281.   (if (eq eof-error-p 'scheme)
  282.       (scheme-read input-stream)
  283.       (progn
  284.         (unless input-stream
  285.           (setq input-stream *standard-input*))
  286.         (check-eof (scheme-read input-stream)
  287.                    input-stream eof-error-p eof-value))))
  288.  
  289. (defun fresh-line (&optional output-stream)
  290.   (unless output-stream
  291.     (setq output-stream *standard-output*))
  292.   (check-output-stream 'fresh-line output-stream)
  293.   (if (output-port? output-stream)
  294.       (scheme-fresh-line output-stream)
  295.       (funcall output-stream 'fresh-line)))
  296.  
  297. (defun terpri (&optional output-stream)
  298.   (unless output-stream
  299.     (setq output-stream *standard-output*))
  300.   (check-output-stream 'terpri output-stream)
  301.   (if (output-port? output-stream)
  302.       (newline output-stream)
  303.       (funcall output-stream 'write-char #\newline))
  304.   nil)
  305.  
  306. (defun write-char (char &optional stream)
  307.   (check-type char character)
  308.   (check-output-stream 'write-char stream)
  309.   (if (or (null stream) (output-port? stream))
  310.       (scheme-write-char char stream)
  311.       (funcall stream 'write-char char))
  312.   char)
  313.  
  314. (defun-clcp write-string-internal (string stream start end)
  315.   (check-type string string)
  316.   (check-output-stream 'write-string stream)
  317.   (if (or (null stream) (output-port? stream))
  318.       (display-substring stream string start end)
  319.       (funcall stream 'write-string string start end))
  320.   string)
  321.  
  322. (defmacro write-string (string &optional stream &rest keywords)
  323.   `(write-string-internal ,string ,stream .
  324.                           ,(parse-keywords '(:start :end) keywords)))
  325.  
  326. (defun write-line (string &optional stream)
  327.   (check-type string string)
  328.   (write-string string stream)
  329.   (terpri stream)
  330.   string)
  331.  
  332. (defun-clcp %%write (object stream escape pretty)
  333.   (unless stream
  334.     (setq stream (current-output-port)))
  335.   (if pretty
  336.       ;; Warning! This won't work for CL streams.
  337.       (pp object stream)
  338.       (let ((class (%%structurep object)))
  339.         (cond
  340.  
  341.           (class
  342.             (let ((print-function (eval (get class 'print-function))))
  343.               (if print-function
  344.                   (fluid-let ((*print-escape* escape))
  345.                     (print-function object stream nil))
  346.                   (begin
  347.                     (write-string "#<" stream)
  348.                     (write-string (symbol-name class) stream)
  349.                     (write-string ">" stream)))))
  350.  
  351.           ;; Deal with composite objects first, since it may be necessarily
  352.           ;; to recursively invoke WRITE.
  353.  
  354.           ((vectorp object)
  355.            (write-string "#(" stream)
  356.            (dotimes (i (vector-length object))
  357.              (unless (zerop i)
  358.                (write-char #\space stream))
  359.              (%%write (vector-ref object i) stream escape pretty))
  360.            (write-string ")" stream))
  361.  
  362.           ((consp object)
  363.            (write-char #\( stream)
  364.            (do ((l object (cdr l)))
  365.                ((null l))
  366.              (unless (consp l)
  367.                (write-string " . " stream)
  368.                (%%write l stream escape pretty)
  369.                (return nil))
  370.              (unless (eq l object)
  371.                (write-char #\space stream))
  372.              (%%write (car l) stream escape pretty))
  373.            (write-char #\) stream))
  374.  
  375.           ;; From this point onward, the CLCP version of WRITE should
  376.           ;; output exactly the same characters as the Scheme version,
  377.           ;; however CL streams are supported.
  378.  
  379.           ;; At some point in the future, we may require all streams
  380.           ;; to be encapsulated, in which case the following clause
  381.           ;; should be removed.
  382.  
  383.           ((output-port? stream)
  384.            (funcall (if escape scheme-write display) object stream))
  385.  
  386.           ((null object)
  387.            (write-string "()" stream))
  388.           ((integerp object)
  389.            (write-string (number->string object '(int (radix d s))) stream))
  390.           ((floatp object)
  391.            (write-string (number->string object '(flo h)) stream))
  392.  
  393.           ((eof-object? object)
  394.            (write-string "#<Scheme EOF>" stream))
  395.           ((input-port? object)
  396.            (write-string "#<Scheme input port>" stream))
  397.           ((output-port? object)
  398.            (write-string "#<Scheme output port>" stream))
  399.  
  400.           (else
  401.  
  402.             (flet ((write-quoted-string (string quote-char)
  403.                      (write-char quote-char stream)
  404.                      (dotimes (i (string-length string))
  405.                        (let ((char (char string i)))
  406.                          (when (or (char= char #\\) (char= char quote-char))
  407.                            (write-char #\\ stream))
  408.                          (write-char char stream)))
  409.                      (write-char quote-char stream)))
  410.  
  411.               (cond ((stringp object)
  412.                      (if (not escape)
  413.                          (write-string object stream)
  414.                          (write-quoted-string object #\")))
  415.  
  416.                     ((symbolp object)
  417.                      (let* ((string (symbol->string object))
  418.                             (length (string-length string)))
  419.                        (cond ((not escape)
  420.                               (write-string string stream))
  421.                              ((dotimes (i length)
  422.                                 (let ((char (char string i)))
  423.                                   (when (or (char-whitespace? char)
  424.                                             (char-lower-case? char))
  425.                                     (return t))))
  426.                               (write-quoted-string string #\|))
  427.                              (else
  428.                                (dotimes (i length)
  429.                                  (let ((char (char string i)))
  430.                                    (when (or (char= char #\\) (char= char #\|))
  431.                                      (write-char #\\ stream))
  432.                                    (write-char char stream)))))))
  433.  
  434.                     (else
  435.                       (write-string "#<CLCP unprintable>" stream))))))))
  436.   object)
  437.  
  438. ;; The default value of :ESCAPE should be the value of *PRINT-ESCAPE*.
  439. ;; For now, just make it be T.
  440.  
  441. ;; However, must extend PARSE-KEYWORDS to indicate when a keyword is not
  442. ;; present so that the default value can be used.  For now, just kludge it.
  443.  
  444. (defmacro write (object &rest keywords)
  445.   (let* ((parsed (parse-keywords '(:stream :escape :pretty) keywords)))
  446.     `(%%write ,object
  447.               ,(first parsed)
  448.               ,(if (member ':escape keywords) (second parsed) t)
  449.               ,(third parsed))))
  450.  
  451. (defun read-from-string-internal (string eof-error-p eof-value start end)
  452.   (let ((length (length string)))
  453.     (when (or (and start (> start 0))
  454.               (and end (< end length)))
  455.       (setq string (subseq string (or start 0) (or end length)))))
  456.   (read (open-input-string string) eof-error-p eof-value))
  457.  
  458. (defmacro read-from-string (string &optional (eof-error-p t) eof-value
  459.                                    &rest keywords)
  460.   `(read-from-string-internal ,string ,eof-error-p ,eof-value .
  461.                               ,(parse-keywords '(:start :end) keywords)))
  462.  
  463. ;; File streams
  464.  
  465. (define :direction ':direction)
  466. (define :input     ':input)
  467. (define :output    ':output)
  468.  
  469. (defmacro with-open-file (descriptor &body body)
  470.   (let ((stream    (first descriptor))
  471.         (file      (second descriptor))
  472.         (flag      (third descriptor))
  473.         (direction (fourth descriptor)))
  474.     (cond ((null (cddr descriptor))
  475.            (setq flag :direction)
  476.            (setq direction :input))
  477.           ((not (eq flag :direction))
  478.            (error "Unknown flag" flag))
  479.           ((not (member direction '(:input :output)))
  480.            (error "Unknown direction" direction)))
  481.     (cond ((eq stream '*standard-input*)
  482.            (unless (eq direction :input)
  483.              (error "Can't bind *STANDARD-INPUT* to a file being ~
  484.                      opened for output."))
  485.            `(with-input-from-file  ,file (lambda () . ,body)))
  486.           ((eq stream '*standard-output*)
  487.            (unless (eq direction :output)
  488.              (error "Can't bind *STANDARD-OUTPUT* to a file being ~
  489.                      opened for input."))
  490.            `(with-output-to-file   ,file (lambda () . ,body)))
  491.           ((eq direction :input)
  492.            `(call-with-input-file  ,file (lambda (,stream) . ,body)))
  493.           ((eq direction :output)
  494.            `(call-with-output-file ,file (lambda (,stream) . ,body)))
  495.           (else
  496.            (error "Shouldn't get here")))))
  497.